package br.com.foxinline.servico;

import br.com.foxinline.enums.*;
import br.com.foxinline.generico.ServicoGenerico;
import br.com.foxinline.modelo.*;
import br.com.foxinline.util.Extenso;
import br.com.foxinline.util.Msg;
import br.com.nordestefomento.jrimum.bopepo.BancoSuportado;
import br.com.nordestefomento.jrimum.bopepo.guia.Guia;
import br.com.nordestefomento.jrimum.bopepo.view.guia.GuiaViewer;
import br.com.nordestefomento.jrimum.domkee.financeiro.banco.febraban.Agencia;
import br.com.nordestefomento.jrimum.domkee.financeiro.banco.febraban.Banco;
import br.com.nordestefomento.jrimum.domkee.financeiro.banco.febraban.Carteira;
import br.com.nordestefomento.jrimum.domkee.financeiro.banco.febraban.ContaBancaria;
import br.com.nordestefomento.jrimum.domkee.financeiro.banco.febraban.TipoDeCobranca;
import br.com.nordestefomento.jrimum.domkee.financeiro.banco.febraban.guia.*;
import br.com.nordestefomento.jrimum.domkee.financeiro.banco.febraban.guia.Contribuinte;
import br.com.nordestefomento.jrimum.utilix.ObjectUtil;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfWriter;
import java.awt.image.BufferedImage;
import java.beans.Transient;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.faces.context.FacesContext;
import javax.imageio.ImageIO;
import javax.persistence.Query;
import org.jrimum.bopepo.BancosSuportados;
import org.jrimum.bopepo.Boleto;
import org.jrimum.bopepo.exemplo.Exemplos;
import org.jrimum.bopepo.pdf.Files;
import org.jrimum.bopepo.view.BoletoViewer;
import org.jrimum.domkee.financeiro.banco.febraban.*;
import org.jrimum.utilix.ClassLoaders;

/**
 *
 * @author luciano
 */
@Stateless
public class IptuServico extends ServicoGenerico<Iptu> {

    public static final String MODELO_PARCELA_SIMPLES_COTA_UNICA_PDF = "/br/com/foxinline/relatorio/iptu/MODELO_PARCELA_NOVO.pdf";//MODELO_PARCELA_SIMPLES_COTA_UNICA.pdf";
    public static final String MODELO_PARCELA_PDF = "/br/com/foxinline/relatorio/iptu/MODELO_PARCELA_NOVO.pdf";
    public static final String MODELO_BRANCO_PDF = "/br/com/foxinline/relatorio/iptu/MODELO_BRANCO.pdf";
    public static final String MODELO_CABECALHO_IPTU_PDF = "/br/com/foxinline/relatorio/iptu/MODELO_CABECALHO_NOVO.pdf";
    public static final String MODELO_BOLETO_IPTU_PDF = "/br/com/foxinline/relatorio/iptu/BOLETO1.pdf";
    @EJB
    private ImovelServico imovelServico;
    @EJB
    private ParcelaServico parcelaServico;
    @EJB
    private PrefeituraServico prefeituraServico;
    @EJB
    private ConfigIptuServico configIptuServico;
    private Long quantidadeRegistrosResults;

    public IptuServico() {
        super(Iptu.class);
    }

    public List<Parcela> gerarParcelas(Imovel imovel, BigDecimal valor, int qtdParcelas, Date date) {
        List<Parcela> parcelas = new ArrayList<Parcela>(qtdParcelas);

        Calendar calendar = GregorianCalendar.getInstance();
        calendar.setTime(date);
        BigDecimal valorParcelado = valor;

        if (qtdParcelas > 0) {
            valorParcelado = valor.divide(new BigDecimal(qtdParcelas), 2, RoundingMode.HALF_DOWN);
        }

        for (int i = 0; i < qtdParcelas; i++) {
            Parcela parcela = new Parcela();
            parcela.setIndice(i + 1);
            calendar.setTime(date);
            calendar.add(Calendar.MONTH, i);
            parcela.setDataVencimento(calendar.getTime());
            parcela.setValor(valorParcelado);
            parcela.setDataEmissao(date);
            parcela.setPago(Boolean.FALSE);
            parcela.setBaixaManual(Boolean.FALSE);
            parcelas.add(parcela);
        }

        Parcela parcela = new Parcela();
        parcela.setIndice(0);
        parcela.setDataVencimento(date);
        parcela.setDataEmissao(date);
        parcela.setValor(valor.multiply(BigDecimal.valueOf(0.9)).setScale(2, RoundingMode.HALF_DOWN));
        parcela.setPago(Boolean.FALSE);
        parcela.setBaixaManual(Boolean.FALSE);
        parcelas.add(parcela);
        return parcelas;
    }

    public List<Imovel> existeUnidade(Imovel i) {
        String sql = "SELECT i FROM Imovel i WHERE i.inscricaoCadastralD = :inscricaoCadastralD and "
                + " i.inscricaoCadastralD = :inscricaoCadastralD and "
                + " i.inscricaoCadastralD = :inscricaoCadastralS and "
                + " i.inscricaoCadastralD = :inscricaoCadastralQuadra and "
                + " i.inscricaoCadastralLote = :inscricaoCadastralLote ";

        Query q = getEntityManager().createQuery(sql);
        q.setParameter("inscricaoCadastralD", i.getInscricaoCadastralD());
        q.setParameter("inscricaoCadastralS", i.getInscricaoCadastralS());
        q.setParameter("inscricaoCadastralQuadra", i.getInscricaoCadastralQuadra());
        q.setParameter("inscricaoCadastralLote", i.getInscricaoCadastralLote());
        return (List<Imovel>) q.getResultList();
    }

    public BigDecimal areaconstruidaUnidades(Imovel i) {
        String sql = "SELECT sum(i.area_da_unidade) FROM Imovel i WHERE i.inscricaoCadastralD = :inscricaoCadastralD and "
                + " i.inscricaoCadastralD = :inscricaoCadastralD and "
                + " inscricaoCadastralD = :inscricaoCadastralS and "
                + " inscricaoCadastralD = :inscricaoCadastralQuadra and "
                + " inscricaoCadastralLote = :inscricaoCadastralLote ";

        Query q = getEntityManager().createQuery(sql);
        q.setParameter("inscricaoCadastralD", i.getInscricaoCadastralD());
        q.setParameter("inscricaoCadastralS", i.getInscricaoCadastralS());
        q.setParameter("inscricaoCadastralQuadra", i.getInscricaoCadastralQuadra());
        q.setParameter("inscricaoCadastralLote", i.getInscricaoCadastralLote());
        return (BigDecimal) q.getSingleResult();
    }

    public BigDecimal vve(Imovel i, ConfigIptu ci) {
        Dimensao dimensao = i.getDimensoes();
        if (dimensao == null) {
            return BigDecimal.ZERO;
        }
        if (dimensao.getAreaUnidade() == null) {
            return BigDecimal.ZERO;
        }
        if (dimensao.getAreaUnidade().compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal calc = dimensao.getAreaUnidade().multiply(vm2edificacao(ci));

        calc = calc.multiply(caracteristicasImovel(i, ci));
        calc = calc.multiply(conservacao(i, ci));
        calc = calc.multiply(subtipo(i, ci));
        return calc;
    }

    public BigDecimal subtipo(Imovel i, ConfigIptu ci) {
        if (i.getPosicao() != null && i.getFachada() != null) {

            if (i.getPosicao() == Posicao.ISOLADA && i.getFachada() == Fachada.ALINHADA) {
                return ci.getIsoladaAlinhada();
            } else if (i.getPosicao() == Posicao.ISOLADA && i.getFachada() == Fachada.RECUADA) {
                return ci.getIsoladaRecuada();
            } else if (i.getPosicao() == Posicao.GEMINADA && i.getFachada() == Fachada.ALINHADA) {
                return ci.getGeminadaAlinhada();
            } else if (i.getPosicao() == Posicao.GEMINADA && i.getFachada() == Fachada.RECUADA) {
                return ci.getGeminadaRecuada();
            } else if (i.getPosicao() == Posicao.SUPERPOSTA && i.getFachada() == Fachada.ALINHADA) {
                return ci.getSuperpostaAlinhada();
            } else if (i.getPosicao() == Posicao.SUPERPOSTA && i.getFachada() == Fachada.RECUADA) {
                return ci.getSuperpostaRecuada();
            } else if (i.getPosicao() == Posicao.CONJUGADA && i.getFachada() == Fachada.ALINHADA) {
                return ci.getConjugadaAlinhada();
            } else if (i.getPosicao() == Posicao.CONJUGADA && i.getFachada() == Fachada.RECUADA) {
                return ci.getConjugadaRecuada();
            }

        }
        return ci.getGeminadaAlinhada();
    }

    public BigDecimal conservacao(Imovel i, ConfigIptu ci) {
        if (i.getEstadoConservacao() != null) {
            if (i.getEstadoConservacao() == EstadoConservacao.BOM) {
                return ci.getBom();
            } else if (i.getEstadoConservacao() == EstadoConservacao.REGULAR) {
                return ci.getRegular();
            } else if (i.getEstadoConservacao() == EstadoConservacao.MAU) {
                return ci.getMau();
            }
        } else {
            return ci.getMau();
        }
        return ci.getMau();
    }

    public BigDecimal caracteristicasImovel(Imovel i, ConfigIptu ci) {
        int calc = 0;

        //REVESTIMENTO
        if (i.getRevExterno() != null) {
            if (i.getRevExterno() == RevExterno.SEM_REVESTIMENTO) {
                calc = 0;
            } else if (i.getRevExterno() == RevExterno.OLEO) {
                calc += Double.parseDouble(ci.getRevestimentoOleo().toString());
            } else if (i.getRevExterno() == RevExterno.CAIACAO) {
                calc += Double.parseDouble(ci.getRevestimentoCaiacao().toString());
            } else if (i.getRevExterno() == RevExterno.MADEIRA) {
                calc += Double.parseDouble(ci.getRevestimentoMadeira().toString());
            } else if (i.getRevExterno() == RevExterno.OUTROS) {
                calc += Double.parseDouble(ci.getRevestimentoOutros().toString());
            }
        } else {
            calc += 0;
        }

        //PISOS
        if (i.getPiso() != null) {
            if (i.getPiso() == Piso.TERRA_BATIDA) {
                calc += Double.parseDouble(ci.getPisosTerraBatida().toString());
            } else if (i.getPiso() == Piso.CIMENTO) {
                calc += Double.parseDouble(ci.getPisosCimento().toString());
            } else if (i.getPiso() == Piso.CERAMICA_OU_MOSAICO) {
                calc += Double.parseDouble(ci.getPisosCeramicoMosaico().toString());
            } else if (i.getPiso() == Piso.OUTROS) {
                calc += Double.parseDouble(ci.getPisosOutros().toString());
            }
        } else {
            calc += 0;
        }

        //FORROS
        if (i.getForro() != null) {
            if (i.getForro() == Forro.INEXISTENTE) {
                calc += Double.parseDouble(ci.getForroInexistente().toString());
            } else if (i.getForro() == Forro.MADEIRA) {
                calc += Double.parseDouble(ci.getForroMadeira().toString());
            } else if (i.getForro() == Forro.ESTUQUE) {
                calc += Double.parseDouble(ci.getForroEstuque().toString());
            } else if (i.getForro() == Forro.LAJE) {
                calc += Double.parseDouble(ci.getForroLaje().toString());
            }
        } else {
            calc += 0;
        }

        //COBERTURA
        if (i.getCobertura() != null) {
            if (i.getCobertura() == Cobertura.PALHA_ZINCO_CAVACO) {
                calc += Double.parseDouble(ci.getCoberturaPalhaZinco().toString());
            } else if (i.getCobertura() == Cobertura.FIBRO_CIMENTO) {
                calc += Double.parseDouble(ci.getCoberturaFibraCimento().toString());
            } else if (i.getCobertura() == Cobertura.TELHA_BARRO) {
                calc += Double.parseDouble(ci.getCoberturaTelha().toString());
            } else if (i.getCobertura() == Cobertura.LAJE) {
                calc += Double.parseDouble(ci.getCoberturaLaje().toString());
            }
        } else {
            calc += Double.parseDouble(ci.getCoberturaPalhaZinco().toString());
        }

        //INSTALACOES SANITÁRIAS
        if (i.getInstSanitaria() != null) {
            if (i.getInstSanitaria() == InstSanitaria.INEXISTENTE) {
                calc += Double.parseDouble(ci.getInstalacoesSanitariasInexistente().toString());
            } else if (i.getInstSanitaria() == InstSanitaria.EXTERNA) {
                calc += Double.parseDouble(ci.getInstalacoesSanitariasExterna().toString());
            } else if (i.getInstSanitaria() == InstSanitaria.INTERNA) {
                calc += Double.parseDouble(ci.getInstalacoesSanitariasInterna().toString());
            } else if (i.getInstSanitaria() == InstSanitaria.MAIS_DE_UMA_INTERNA) {
                calc += Double.parseDouble(ci.getInstalacoesSanitariasMaisdeUma().toString());
            }
        } else {
            calc += 0;
        }

        //INSTALACOES ELETRICAS
        if (i.getInstalacoesEletricas() != null) {

            if (i.getInstalacoesEletricas() == InstalacoesEletricas.INEXISTENTE) {
                calc += Double.parseDouble(ci.getInstalacoesEletricasInexistente().toString());
            } else if (i.getInstalacoesEletricas() == InstalacoesEletricas.APARENTE) {
                calc += Double.parseDouble(ci.getInstalacoesEletricasAparente().toString());
            } else if (i.getInstalacoesEletricas() == InstalacoesEletricas.EMBUTIDA) {
                calc += Double.parseDouble(ci.getInstalacoesEletricasEmbutida().toString());
            }

        } else {
            calc += 0;
        }

        //CONCRETO
        if (i.getEstrutura() != null) {
            if (i.getEstrutura() == Estrutura.CONCRETO) {
                calc += Double.parseDouble(ci.getEstruturaConcreto().toString());
            } else if (i.getEstrutura() == Estrutura.ALVENARIA) {
                calc += Double.parseDouble(ci.getEstruturaAlvenaria().toString());
            } else if (i.getEstrutura() == Estrutura.MADEIRA) {
                calc += Double.parseDouble(ci.getEstruturaMadeira().toString());
            } else if (i.getEstrutura() == Estrutura.METALICA) {
                calc += Double.parseDouble(ci.getEstruturaMetalica().toString());
            }

        } else {
            calc += Double.parseDouble(ci.getEstruturaMadeira().toString());
        }

        return new BigDecimal(calc).divide(new BigDecimal("100"));

    }

    public BigDecimal vm2edificacao(ConfigIptu ci) {
        return ci.getVm2e();
    }

    public BigDecimal vvt(Imovel bci, ConfigIptu ci) {
        BigDecimal calc = areadoterreno(bci).multiply(vm2terreno(bci, ci)).multiply(fatorLocalizacao(bci)).multiply(topografia(bci, ci)).multiply(situacao(bci, ci)).multiply(pedologia(bci, ci));
        return calc;
    }

    public BigDecimal pedologia(Imovel i, ConfigIptu ci) {
        if (i.getPedologia() == null) {
            return ci.getPedologiaAlagado();
        }
        if (i.getPedologia() == Pedologia.ALAGADO) {
            return ci.getPedologiaAlagado();
        } else if (i.getPedologia() == Pedologia.INUNDAVEL) {
            return ci.getPedologiaInundavel();
        } else if (i.getPedologia() == Pedologia.ROCHOSO) {
            return ci.getPedologiaRochoso();
        } else if (i.getPedologia() == Pedologia.NORMAL) {
            return ci.getPedologiaNormal();
        } else if (i.getPedologia() == Pedologia.ARENOSO) {
            return ci.getPedologiaArenoso();
        } else {
            return ci.getPedologiaAlagado();
        }
    }

    public BigDecimal situacao(Imovel i, ConfigIptu ci) {
        if (i.getSituacaoTerreno() == null) {
            return ci.getSituacaoEncravado();
        }
        if (i.getSituacaoTerreno() == SituacaoTerreno.ESQUINA_OU_DUAS_FRENTES) {
            return ci.getSituacaoEsquina();
        } else if (i.getSituacaoTerreno() == SituacaoTerreno.UMA_FRENTE) {
            return ci.getSituacaoUmaFrente();
        } else if (i.getSituacaoTerreno() == SituacaoTerreno.ENCRAVADO_OU_VILA) {
            return ci.getSituacaoEncravado();
        } else {
            return ci.getSituacaoEncravado();
        }
    }

    public BigDecimal topografia(Imovel i, ConfigIptu ci) {
        if (i.getTopografia() == null) {
            return ci.getTopografiaDeclive();
        }
        if (i.getTopografia() == Topografia.PLANO) {
            return ci.getTopografiaPlano();
        } else if (i.getTopografia() == Topografia.ACLIVE) {
            return ci.getTopografiaAclive();
        } else if (i.getTopografia() == Topografia.DECLIVE) {
            return ci.getTopografiaDeclive();
        }
        return ci.getTopografiaDeclive();
    }

    public BigDecimal fatorLocalizacao(Imovel i) {
        if (i.getLocalizacao() == null) {
            return new BigDecimal(FatorLocalizacao.BAIXO.getValor()).divide(new BigDecimal("100"));
        }
        return new BigDecimal(i.getLocalizacao().getValor()).divide(new BigDecimal("100"));
    }

    public BigDecimal vm2terreno(Imovel i, ConfigIptu ci) {
        if (i.getOcupacao() != null && (i.getOcupacao() == Ocupacao.NAO_CONSTRUIDO || i.getOcupacao() == Ocupacao.CONSTRUCAO_ANDAMENTO)) {
            return ci.getNaoEdificado();
        }
        return ci.getEdificado();
    }

    public BigDecimal areadoterreno(Imovel i) {
        List<Imovel> exist = existeUnidade(i);
        Dimensao dimensao = i.getDimensoes();
        if (dimensao == null || dimensao.getAreaTerreno() == null) {
            return BigDecimal.ZERO;
        }
        if (i.getUtilizacao() != null && i.getUtilizacao() == Utilizacao.TERRENO_SEM_USO || (exist == null || exist.isEmpty() || exist.size() == 1)) {
            return dimensao.getAreaTerreno();
        } else if (dimensao.getAreaUnidade() != null && dimensao.getAreaUnidade().compareTo(BigDecimal.ZERO) == 1) {
            BigDecimal fi = dimensao.getAreaTerreno().multiply(dimensao.getAreaUnidade());

            if (dimensao.getAreaTotalEdificacao().compareTo(BigDecimal.ZERO) == 0) {
                return fi.divide(areaconstruidaUnidades(i), 2, RoundingMode.HALF_UP);
            } else {
                return fi.divide(dimensao.getAreaTotalEdificacao(), 2, RoundingMode.HALF_UP);
            }
        } else {
            return dimensao.getAreaTerreno();
        }
    }

    public BigDecimal iptuAvista(Imovel imovel, DadosIptu dadosIptu) {
        return null;//iptu(imovel, dadosIptu).multiply(new BigDecimal(0.9));
    }

    /**
     * Get todos os iptus referente ao imóvel.
     *
     * @param imovel
     * @return Lista de iptus do imovel.
     */
    public List<Iptu> getIptus(Imovel imovel) {
        String sql = "SELECT i FROM Iptu i WHERE i.imovel.id = :id order by i.dataIptu desc";
        Query query = getEntityManager().createQuery(sql);
        query.setParameter("id", imovel.getId());
        return (List<Iptu>) query.getResultList();
    }

    public BigDecimal indiceReferencia(ConfigIptu config) {
        return (config.getValorReferencia().divide(new BigDecimal("1.165"), 2, RoundingMode.HALF_UP));
    }

    public BigDecimal tcc(Dimensao d, ConfigIptu config) {
        if (d == null || d.getTestadaImovel() == null) {
            return (indiceReferencia(config)).multiply(new BigDecimal("0.02")).divide(new BigDecimal("1.0641"), 2, RoundingMode.HALF_UP);
        }
        return (indiceReferencia(config).multiply(d.getTestadaImovel())).multiply(new BigDecimal("0.02")).divide(new BigDecimal("1.0641"), 2, RoundingMode.HALF_UP);
    }

    public BigDecimal tcl(Dimensao d, ConfigIptu config) {
        if (d == null || d.getAreaUnidade() == null) {
            return (indiceReferencia(config)).multiply(new BigDecimal("0.01")).divide(new BigDecimal("1.0641"), 2, RoundingMode.HALF_UP);
        }

        return (indiceReferencia(config).multiply(d.getAreaUnidade())).multiply(new BigDecimal("0.01")).divide(new BigDecimal("1.0641"), 2, RoundingMode.HALF_UP);
    }

    public BigDecimal tlp(Dimensao d, ConfigIptu config) {
        if (d == null || d.getTestadaImovel() == null) {
            return (indiceReferencia(config)).multiply(new BigDecimal("0.02")).divide(new BigDecimal("1.0641"), 2, RoundingMode.HALF_UP);
        }
        return (indiceReferencia(config).multiply(d.getTestadaImovel())).multiply(new BigDecimal("0.02")).divide(new BigDecimal("1.0641"), 2, RoundingMode.HALF_UP);
    }

    private Calendar addMes(Calendar calendar, Date vencimento) {
        calendar.setTime(vencimento);
        calendar.add(Calendar.MONTH, 1);
        return calendar;
    }

    private void boleto() throws IOException {
        Boleto boletoBBNossoNumero10 = crieBoletoBBNossoNumero10();
        Boleto boletoBradesco = crieBoletoBradesco();

        List<Boleto> boletos = new ArrayList<Boleto>();
        boletos.add(boletoBBNossoNumero10);
        boletos.add(boletoBBNossoNumero10);
        boletos.add(boletoBBNossoNumero10);

        //Informando o template personalizado:
        File templatePersonalizado = new File(ClassLoaders.getResource("/br/com/foxinline/relatorio/iptu/BoletoCarne3PorPagina.pdf").getFile());
        //Agrupando os boletos em apenas uma página
        File boletosPorPagina = groupInPages(boletos, "Carne3PorPagina.pdf", templatePersonalizado);
        try {
            RelatorioServico.genReport(Files.fileToBytes(boletosPorPagina));


        } catch (IOException ex) {
            Logger.getLogger(IptuServico.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private BigDecimal ufmp(Integer qtd, ConfigIptu config) {
        return config.getUfmp().multiply(new BigDecimal(qtd));
    }

    public BigDecimal aliquota(Imovel i, BigDecimal vvi, ConfigIptu config) {

        if (i.getUtilizacao() != null) {
            if (i.getUtilizacao() == Utilizacao.TERRENO_SEM_USO) {
                return (new BigDecimal("1.5"));
            } else if (i.getUtilizacao() == Utilizacao.RESIDENCIAL && vvi.compareTo(ufmp(800, config)) == -1) {
                return new BigDecimal("1");
            } else if (i.getUtilizacao() == Utilizacao.RESIDENCIAL && vvi.compareTo(ufmp(800, config)) == 1 && vvi.compareTo(ufmp(8700, config)) == -1) {
                return (new BigDecimal("1"));

            } else if (i.getUtilizacao() == Utilizacao.RESIDENCIAL && (vvi.compareTo(ufmp(8700, config)) == 1
                    || vvi.compareTo(ufmp(8700, config)) == 0)) {
                return new BigDecimal("1.5");
            } else if ((i.getUtilizacao() == Utilizacao.COMERCIAL || i.getUtilizacao() == Utilizacao.PRESTACAO_DE_SERVICO
                    || i.getUtilizacao() == Utilizacao.INDUSTRIAL)
                    && (vvi.compareTo(ufmp(2900, config)) == 0 || vvi.compareTo(ufmp(2900, config)) == -1)) {
                return new BigDecimal("1");

            } else if ((i.getUtilizacao() == Utilizacao.COMERCIAL
                    || i.getUtilizacao() == Utilizacao.PRESTACAO_DE_SERVICO
                    || i.getUtilizacao() == Utilizacao.INDUSTRIAL)
                    && (vvi.compareTo(ufmp(2900, config)) == 1)) {
                return new BigDecimal("1.5");
            }
        }

        return (new BigDecimal("1"));
    }

    /**
     * TODO - verificar o calculo Area do Terreno
     */
    public int qtdParcelas(BigDecimal valor, ConfigIptu ci) {
        if ((valor.divide(new BigDecimal(3), BigDecimal.ROUND_HALF_UP)).compareTo(ci.getValorMinimoParcela()) >= 0) {
            return 3;
        } else if ((valor.divide(new BigDecimal(2), BigDecimal.ROUND_HALF_UP)).compareTo(ci.getValorMinimoParcela()) >= 0) {
            return 2;
        }
        return 0;
    }

//####################################################### BOLETO ########################################################
    public void gerarBoleto() throws IOException, Exception {
        gerarGuia(null);
        boleto();
    }

    public void gerarBoleto(List<Imovel> imoveis) throws IOException, Exception {
        gerarGuia(imoveis);
        boleto();
    }

    public void gerarBoleto(Imovel imovel) throws IOException, Exception {
        List<Imovel> imoveis = new ArrayList<Imovel>();
        imoveis.add(imovel);
        gerarGuia(imoveis);
        boleto();

    }

    private static File groupInPages(List<Boleto> boletos, String filePath, File templatePersonalizado) throws IOException {
        File arq = null;
        BoletoViewer boletoViewer = new BoletoViewer(boletos.get(0));
        boletoViewer.setTemplate(templatePersonalizado);

        List<byte[]> boletosEmBytes = new ArrayList<byte[]>(boletos.size() + 1);

        //Adicionando os PDF, em forma de array de bytes, na lista.
        for (Boleto bop : boletos) {
            boletosEmBytes.add(boletoViewer.setBoleto(bop).getPdfAsByteArray());
        }

        try {
            //Criando o arquivo com os boletos da lista
            arq = Files.bytesToFile(filePath, mergeFilesInPages(boletosEmBytes));
        } catch (Exception e) {
            throw new IllegalStateException("Erro durante geração do PDF! Causado por " + e.getLocalizedMessage(), e);
        }

        return arq;
    }

    private static Boleto crieBoletoBBNossoNumero10() {

        Titulo titulo = Exemplos.crieTitulo();

        org.jrimum.domkee.financeiro.banco.febraban.ContaBancaria contaBancaria = titulo.getContaBancaria();
        contaBancaria.setBanco(BancosSuportados.BANCO_DO_BRASIL.create());
        contaBancaria.setNumeroDaConta(new NumeroDaConta(1234567));
        contaBancaria.setCarteira(new org.jrimum.domkee.financeiro.banco.febraban.Carteira(12));

        titulo.setNossoNumero("1234567890");
        Boleto boletoBBNossoNumero10 = Exemplos.crieBoleto(titulo);

        return boletoBBNossoNumero10;
    }

    private static Boleto crieBoletoBradesco() {
        Titulo titulo = Exemplos.crieTitulo();

        org.jrimum.domkee.financeiro.banco.febraban.ContaBancaria contaBancaria = titulo.getContaBancaria();
        contaBancaria.setBanco(BancosSuportados.BANCO_BRADESCO.create());
        contaBancaria.setAgencia(new org.jrimum.domkee.financeiro.banco.febraban.Agencia(Integer.SIZE));
        contaBancaria.setCarteira(new org.jrimum.domkee.financeiro.banco.febraban.Carteira(12));
        titulo.setNossoNumero("01234567891");
        contaBancaria.setNumeroDaConta(new NumeroDaConta(1234567));

        Boleto boletoBradesco = Exemplos.crieBoleto(titulo);

        return boletoBradesco;
    }

    public static byte[] mergeFilesInPages(List<byte[]> pdfFilesAsByteArray) throws DocumentException, IOException {

        Document document = new Document();
        ByteArrayOutputStream byteOS = new ByteArrayOutputStream();

        PdfWriter writer = PdfWriter.getInstance(document, byteOS);

        document.open();

        PdfContentByte cb = writer.getDirectContent();
        float positionAnterior = 0;



        //Para cada arquivo da lista, cria-se um PdfReader, responsável por ler o arquivo PDF e recuperar informações dele.
        for (byte[] pdfFile : pdfFilesAsByteArray) {

            PdfReader reader = new PdfReader(pdfFile);


            //Faz o processo de mesclagem por página do arquivo, começando pela de número 1.
            for (int i = 1; i <= reader.getNumberOfPages(); i++) {

                float documentHeight = cb.getPdfDocument().getPageSize().getHeight();

                //Importa a página do PDF de origem
                PdfImportedPage page = writer.getImportedPage(reader, i);
                float pagePosition = positionAnterior;

                /*
                 * Se a altura restante no documento de destino form menor que a
                 * altura do documento, cria-se uma nova página no documento de
                 * destino.
                 */
                if ((documentHeight - positionAnterior) <= page.getHeight()) {
                    document.newPage();
                    pagePosition = 0;
                    positionAnterior = 0;
                }

                //Adiciona a página ao PDF destino
                cb.addTemplate(page, 0, pagePosition);

                positionAnterior += page.getHeight();
            }
        }

        byteOS.flush();
        document.close();

        byte[] arquivoEmBytes = byteOS.toByteArray();
        byteOS.close();

        return arquivoEmBytes;
    }

    public boolean imovelValido(Imovel imovel) {
        if (imovel == null) {
            return false;
        } else if (imovel.getAtivo() != null && !imovel.getAtivo()) {
            return false;
        } else if (imovel.getProprietarios() == null || imovel.getProprietarios().size() <= 0) {
            return false;
        } else if (imovel.getInscricaoCanceladaPorDesmembramento() != null && imovel.getInscricaoCanceladaPorDesmembramento()) {
            return false;
        }
        return true;
    }

    private static File groupInPagesGuia(List<GuiaViewer> guiaViewers, String filePath, File templatePersonalizado) throws IOException {
        int i = 0;
        File arq = null;
        List<byte[]> boletosEmBytes = new ArrayList<byte[]>(guiaViewers.size() + guiaViewers.size() / 4);

        while (guiaViewers.size() > i) {

            GuiaViewer guiaViewer = guiaViewers.get(i);
            Guia boletos = guiaViewer.getGuia();


            File bo = new File(ClassLoaders.getResource(MODELO_CABECALHO_IPTU_PDF).getFile());
            String localizacao = boletos.getTextosExtras().get("txtInscricao");
            int j;
            //boletosEmBytes.add();
            //Adicionando os PDF, em forma de array de bytes, na lista.
            for (j = i; j < guiaViewers.size() && localizacao.equals(guiaViewers.get(j).getGuia().getTextosExtras().get("txtInscricao")); j++) {
                guiaViewer = guiaViewers.get(j);
                if (guiaViewer.getTemplate() == null) {
                    guiaViewer.setTemplate(templatePersonalizado);
                }
                boletosEmBytes.add(guiaViewers.get(j).getPdfAsByteArray());
            }

            guiaViewer.setTemplate(bo);
            boletosEmBytes.add(guiaViewer.getPdfAsByteArray());

            i = j;
        }

        try {
            //Criando o arquivo com os boletos da lista
            arq = Files.bytesToFile(filePath, mergeFilesInPages(boletosEmBytes));
        } catch (Exception e) {
            throw new IllegalStateException("Erro durante geração do PDF! Causado por " + e.getLocalizedMessage(), e);
        }

        return arq;
    }

    public void gerarGuia(List<Imovel> imoveis) throws Exception {
        gerarGuia(imoveis, null, null);
    }

    public void gerarGuia(List<Imovel> imoveis, Iptu iptuParam, Parcela parcelaParam) throws Exception {

        String unica = "ÚNICA";

        File templatePersonalizado = new File(ClassLoaders.getResource(MODELO_BOLETO_IPTU_PDF).getFile());

        if (imoveis == null || imoveis.isEmpty()) {
            imoveis = imovelServico.findAll();
        }

        List<GuiaViewer> guiaViewers = new ArrayList<GuiaViewer>();
        Prefeitura prefeitura = prefeituraServico.find();
        String agencia[] = prefeitura.getBancoAgencia().split("-");

        List<ConfigIptu> configs = configIptuServico.findAll();
        if (configs.isEmpty()) {
            Msg.messagemError("Configurações do IPTU não cadastradas!");
            return;
        }
        ConfigIptu configIptu = configs.get(0);
        BufferedImage imageOrgaoRecebedor = null;

        if (prefeitura.getLogo() != null && prefeitura.getLogo().getUrl() != null && !prefeitura.getLogo().getUrl().isEmpty()) {
            
            try {
                imageOrgaoRecebedor = ImageIO.read(new File(prefeitura.getLogo().getUrl() + prefeitura.getLogo().getNome()));
            } catch (javax.imageio.IIOException e) {
                imageOrgaoRecebedor = ImageIO.read(new File(FacesContext.getCurrentInstance().getExternalContext().getRealPath("/media/images/fox-avatar.png")));
            }
        }

        for (Imovel imovel : imoveis) {

            if (!imovelValido(imovel)) {
                continue;
            }

            Iptu iptu;

            if (iptuParam == null) {

                List<Iptu> iptus = findIptuByImovelId(imovel.getId());

                if (iptus.isEmpty() || iptus.get(iptus.size() - 1).getImovelIrregular().booleanValue()) {
                    continue;
                }
                iptu = iptus.get(0);
            } else {
                iptu = iptuParam;
                if (iptu.getImovel() != null) {
                    imovel = iptu.getImovel();
                }
            }

            if (iptu.isPago() || (iptu.getBaixaManual() != null && iptu.getBaixaManual())) {
                continue;
            }

            Proprietario proprietario = imovel.getProprietarios().get(0);

            String documento = null;

            String nomeContribuinte = null;

            if (proprietario.getTipoDocumento() != null) {
                if (proprietario.getTipoDocumento().equals(TipoDocumento.CPF)) {
                    documento = proprietario.getCpf();
                    nomeContribuinte = (proprietario.getNome() + "").toUpperCase();
                } else {
                    documento = proprietario.getCnpj();
                    nomeContribuinte = (proprietario.getNomeFantasia() + "").toUpperCase();
                }
            }

            Contribuinte contribuinte = new Contribuinte(nomeContribuinte, documento == null || documento.length() == 0 ? "00000000000" : documento);

            Endereco endereco = imovel.getEnderecoImovel();

            /*
             * ====================================== Informando os dados do orgao/empresa recebedor ======================================
             */

            OrgaoRecebedor orgaoRecebedor = new OrgaoRecebedor(prefeitura.getNome(), TipoSeguimento.PREFEITURA);
            orgaoRecebedor.setCodigoDeIdentificacaoFebraban(new CodigoDeIdentificacaoFebraban(prefeitura.getCodigoFebraban()));
            if (ObjectUtil.isNotNull(imageOrgaoRecebedor)) {
                orgaoRecebedor.setImgLogo(imageOrgaoRecebedor);
            }

            /*
             * ====================================== Informando os dados do convênio ======================================
             */
            Convenio convenio = new Convenio();

            if (agencia.length == 2) {
                convenio.setAgencia(new Agencia(Integer.parseInt(agencia[0]), agencia[1]));
            }

            Banco banco = BancoSuportado.BANCO_DO_BRASIL.create();
            ContaBancaria contaB = new ContaBancaria();

            contaB.setAgencia(new Agencia(Integer.parseInt(agencia[0]), agencia[1]));

            if (prefeitura.getBancoCarteira() != null) {
                Carteira carteira = new Carteira(Integer.parseInt(prefeitura.getBancoCarteira()), TipoDeCobranca.COM_REGISTRO);
                contaB.setCarteira(carteira);
            }

            banco.addContaBancaria(contaB);

            convenio.setBanco(banco);
            convenio.setNumero(Integer.parseInt(prefeitura.getBancoNumeroConvenio()));

            List<Parcela> parcelas;

            if (parcelaParam == null) {
                parcelas = iptu.getParcelas();
            } else {
                parcelas = new ArrayList<Parcela>();
                Date date = new Date();
                if (parcelaParam.getDataVencimento().before(date)) {
                    Calendar calendar = GregorianCalendar.getInstance();
                    if (parcelaParam.getJuros() == null) {
                        parcelaParam.setJuros(BigDecimal.ZERO);
                    }
                    calendar = addMes(calendar, parcelaParam.getDataVencimento());

                    while (calendar.getTime().before(date)) {
                        parcelaParam.setJuros((parcelaParam.getValor().multiply(new BigDecimal(0.01))).add(parcelaParam.getJuros()).setScale(2, RoundingMode.HALF_DOWN));
                        parcelaParam.setValor(parcelaParam.getValor().multiply(new BigDecimal(1.01)).setScale(2, RoundingMode.HALF_DOWN));
                        calendar = addMes(calendar, calendar.getTime());
                    }
                    calendar.setTime(date);
                    calendar.add(Calendar.DAY_OF_MONTH, 5);
                    parcelaParam.setDataVencimento(calendar.getTime());
                    parcelaServico.update(parcelaParam);
                }
                parcelas.add(parcelaParam);
                if (imoveis.isEmpty()) {
                    break;
                } else {
                    imoveis = new ArrayList<Imovel>();
                }
            }

            Collections.sort(parcelas, new Comparator<Parcela>() {
                @Override
                public int compare(Parcela p1, Parcela p2) {
                    return p1.getId().compareTo(p2.getId());
                }
            });

            int quantidadePercelas = parcelas.size();
            Guia guia = new Guia();
            List<GuiaViewer> gs = new ArrayList<GuiaViewer>();

            NumberFormat nf = NumberFormat.getCurrencyInstance();
            DecimalFormatSymbols s = ((DecimalFormat) nf).getDecimalFormatSymbols();
            s.setCurrencySymbol("");
            s.setGroupingSeparator('.');
            s.setMonetaryDecimalSeparator(',');
            ((DecimalFormat) nf).setDecimalFormatSymbols(s);

            if (configIptu.getDesconto() == null) {
                configIptu.setDesconto(BigDecimal.ONE);
            }

            String descontoIptu = nf.format(configIptu.getDesconto().multiply(new BigDecimal(100)));

            for (int i = 0; i < quantidadePercelas; i++) {

                /*
                 * ===Informando dados da Arrecadação ===
                 */
                Parcela parcela = parcelas.get(i);
                Arrecadacao arrecadacao = new Arrecadacao(orgaoRecebedor, contribuinte, convenio);

                if (i == quantidadePercelas - 1) {
                    String nossoNumero = parcela.getId() + "0" + parcela.getIndice();
                    while (nossoNumero.length() < 14) {
                        nossoNumero = "0" + nossoNumero;
                    }

                    nossoNumero = "001" + nossoNumero;
                    arrecadacao.setNossoNumero(nossoNumero);

                    arrecadacao.setValorDocumento(parcelas.get(i).getValor());
                    arrecadacao.setTipoValorReferencia(TipoValorReferencia.QUANTIDADE_DE_MOEDA_COM_DV_MODULO_10);
                    arrecadacao.setDataDoDocumento(GregorianCalendar.getInstance().getTime());

                    if (quantidadePercelas == 1) {
                        templatePersonalizado = new File(ClassLoaders.getResource(MODELO_PARCELA_SIMPLES_COTA_UNICA_PDF).getFile());
                    }

                    arrecadacao.setDataDoVencimento(parcelas.get(0).getDataVencimento());

                } else {
                    templatePersonalizado = new File(ClassLoaders.getResource(MODELO_PARCELA_PDF).getFile());

                    String nossoNumero = parcela.getId() + "0" + parcela.getIndice();
                    while (nossoNumero.length() < 14) {
                        nossoNumero = "0" + nossoNumero;
                    }

                    nossoNumero = "001" + nossoNumero;

                    arrecadacao.setNossoNumero(nossoNumero);
                    arrecadacao.setValorDocumento(parcelas.get(i).getValor());
                    arrecadacao.setTipoValorReferencia(TipoValorReferencia.VALOR_COBRADO_EM_REAL_COM_DV_MODULO_10);
                    arrecadacao.setDataDoDocumento(GregorianCalendar.getInstance().getTime());
                    arrecadacao.setDataDoVencimento(parcelas.get(i).getDataVencimento());
                }

                /*
                 * ====================================== Informando dados da Guia ======================================
                 */

                guia = new Guia(arrecadacao);

                BigDecimal areaTerreno = BigDecimal.ZERO;
                BigDecimal areaTotalEdificacao = BigDecimal.ZERO;
                if (imovel.getDimensoes() != null && imovel.getDimensoes().getAreaTerreno() != null) {
                    areaTerreno = imovel.getDimensoes().getAreaTerreno();
                }
                if (imovel.getDimensoes() != null && imovel.getDimensoes().getAreaTotalEdificacao() != null) {
                    areaTotalEdificacao = imovel.getDimensoes().getAreaTotalEdificacao();
                }

                guia.addTextosExtras("txtMatricula", imovel.getId().toString());
                guia.addTextosExtras("txtIdImovel", imovel.getId().toString());
                guia.addTextosExtras("txtAreaTerreno", nf.format(areaTerreno.setScale(2, RoundingMode.HALF_DOWN)));
                guia.addTextosExtras("txtAreaEdificacao", nf.format(areaTotalEdificacao.setScale(2, RoundingMode.HALF_DOWN)));
                guia.addTextosExtras("txtVve", nf.format(iptu.getVve().setScale(2, RoundingMode.HALF_DOWN)));
                guia.addTextosExtras("txtVvt", nf.format(iptu.getVvt().setScale(2, RoundingMode.HALF_DOWN)));
                guia.addTextosExtras("txtVvi", nf.format(iptu.getVvi().setScale(2, RoundingMode.HALF_DOWN)));
                guia.addTextosExtras("txtAliquota", nf.format(iptu.getAliquota().setScale(2, RoundingMode.HALF_DOWN)));
                guia.addTextosExtras("txtValorBaseCorrigido", nf.format(iptu.getValorBaseCorrigido().setScale(2, RoundingMode.HALF_DOWN)));

                guia.setInstrucaoAoCaixa1("PAGAMENTO SOMENTE À VISTA NO BANCO DO BRASIL.");
                guia.setInstrucaoAoCaixa2("PREFERENCIAMENTE DEVE SER PAGA NOS TERMINAIS DE AUTO-ATENDIMENTO,");
                guia.setInstrucaoAoCaixa3("CORRESPONDENTES BANCÁRIOS E INTERNET");

                // GuiaViewer é o responsável por prover uma visualização da guia.
                GuiaViewer guiaViewer = new GuiaViewer(guia);

                // Se houver um template personalizado, com campos extras, novas informações podem
                // ser adicionadas.
                if (ObjectUtil.isNotNull(templatePersonalizado)) {
                    guia.addTextosExtras("txtCidade", "CIDADE: " + (endereco.getMunicipio() == null ? "Parnaíba" : endereco.getMunicipio()));
                    guia.addTextosExtras("txtEstado", "ESTADO:  " + (endereco.getUf() == null ? "PI" : endereco.getUf()));
                    guia.addTextosExtras("txtCep", "CEP: " + (endereco.getCep() == null ? "" : endereco.getCep()));
                    guia.addTextosExtras("txtEndereco", "" + (endereco.getLogradouro() == null ? "" : endereco.getLogradouro().toUpperCase())
                            + (endereco.getNumero() == null ? " " : " " + endereco.getNumero().toUpperCase())
                            + (endereco.getCep() == null ? " - " : " - " + endereco.getCep().toUpperCase())
                            + (endereco.getMunicipio() == null ? " - Parnaíba" : " - " + endereco.getMunicipio().toUpperCase())
                            + (endereco.getUf() == null ? "/PI" : "/" + endereco.getUf().toUpperCase()));
                    guia.addTextosExtras("txtBairro", "BAIRRO: " + (endereco.getBairro() == null ? "" : endereco.getBairro()));
                    guia.addTextosExtras("txtComplemento", "" + (endereco.getCompl() == null ? "" : endereco.getCompl()));
                    guia.addTextosExtras("txtInscricao", imovel.getId() + "");
                    guia.addTextosExtras("txtValorDocumento2", "R$ " + nf.format(arrecadacao.getValorDocumento()));
                    guia.addTextosExtras("txtValorDocumento1", "R$ " + nf.format(arrecadacao.getValorDocumento()));
                    guia.addTextosExtras("txtLocalizacao", imovelServico.getInscricaoCadastralFormatada(imovel));
                    guia.addTextosExtras("txtLocalizacao1", imovelServico.getInscricaoCadastralFormatada(imovel));

                    if (parcelaParam == null) {
                        guia.addTextosExtras("txtParcela", i == quantidadePercelas - 1 ? unica : i + 1 + "/" + (quantidadePercelas - 1));
                        guia.addTextosExtras("txtParcela1", i == quantidadePercelas - 1 ? unica : i + 1 + "/" + (quantidadePercelas - 1));
                        guia.addTextosExtras("txtParcela2", i == quantidadePercelas - 1 ? "PARCELA ÚNICA" : i + 1 + "ª PARCELA");
                    } else {
                        guia.addTextosExtras("txtParcela", parcelaParam.getIndice() == 0 ? unica : parcelaParam.getIndice() + "");
                        guia.addTextosExtras("txtParcela1", parcelaParam.getIndice() == 0 ? unica : parcelaParam.getIndice() + "");
                        guia.addTextosExtras("txtParcela2", parcelaParam.getIndice() == 0 ? "PARCELA ÚNICA" : parcelaParam.getIndice() + "ª PARCELA");
                    }

                    if (i == quantidadePercelas - 1) {
                        guia.addTextosExtras("txtUnica", new SimpleDateFormat("dd/MM/yyyy").format(arrecadacao.getDataDoVencimento()));
                        guia.addTextosExtras("txtCotaUnica", "O pagamento do IPTU/" + new SimpleDateFormat("yyyy").format(iptu.getDataIptu())
                                + ", em cota única com desconto de " + configIptu.getDesconto().multiply(new BigDecimal(100)) + "%, poderá ser efetuado até o dia "
                                + new SimpleDateFormat("dd/MM/yyyy").format(arrecadacao.getDataDoVencimento()));

                    }

                    guia.addTextosExtras("txtDebitos", debitoIPTU(imovel.getId()));
                    guia.addTextosExtras("txtExercicio", new SimpleDateFormat("yyyy").format(iptu.getDataIptu()));
                    guia.addTextosExtras("txtExercicio2", new SimpleDateFormat("yyyy").format(iptu.getDataIptu()));
                    guia.addTextosExtras("txtAno", new SimpleDateFormat("yyyy").format(iptu.getDataIptu()));
                    guia.addTextosExtras("txtNossoNumero", arrecadacao.getNossoNumero());
                    guia.addTextosExtras("txtNossoNumero1", arrecadacao.getNossoNumero());

                    guia.addTextosExtras("txtNomePrefeitura", prefeitura.getNome().toUpperCase() + "\nSECRETARIA DA FAZENDA\nDOCUMENTO DE ARRECADAÇÃO MUNICIPAL");
                    guia.addTextosExtras("txtNomePrefeitura2", prefeitura.getNome().toUpperCase() + "\nSECRETARIA DA FAZENDA\nDOCUMENTO DE ARRECADAÇÃO MUNICIPAL");
                    guia.addTextosExtras("txtCotaUnica", "O pagamento do IPTU/" + new SimpleDateFormat("yyyy").format(iptu.getDataIptu())
                            + ", em cota única com desconto de " + descontoIptu + "%, poderá ser efetuado até o dia ");

                    if (configIptu.getValorMinimoParcela() == null) {
                        configIptu.setValorMinimoParcela(new BigDecimal(25.40));
                    }

                    Extenso e = new Extenso(configIptu.getValorMinimoParcela());
                    guia.addTextosExtras("txtParcelamento", "O contribuinte que não optar pela cota única, só poderá recolher o imposto, parceladamente,"
                            + " sem benefício de desconto, em até 3 (três) cotas, obedecendo o valor mínimo da parcela, fixada em R$ " + nf.format(configIptu.getValorMinimoParcela()) + " (" + e.toString() + ")");

                    if (prefeitura.getSite() == null) {
                        prefeitura.setSite("");
                    }

                    guia.addTextosExtras("txtAtualizacaoCad", "Mantenha os seus dados cadastrais e de seus imóveis devidamente atualizado atravês do site "
                            + prefeitura.getSite() + " ou na central de atendimento do IPTU localizada na sede da " + prefeitura.getNome() + ".");

                    guia.addTextosExtras("txtParcelaUnicaDesconto", "COTA ÚNICA JÁ REDUZIDA DE " + descontoIptu + "%");
                    guiaViewer = new GuiaViewer(guia);
                    guiaViewer.setTemplate(templatePersonalizado);
                }
                gs.add(guiaViewer);
            }
            if (quantidadePercelas > 4) {

                quantidadePercelas = quantidadePercelas - 4;

                if (quantidadePercelas > 0 && (((quantidadePercelas) % 5) > 0) && quantidadePercelas != 4) {
                    int condicao = (5 - ((quantidadePercelas) % 5));

                    for (int i = 0; i < condicao; i++) {
                        GuiaViewer guiaViewer = new GuiaViewer(guia);
                        templatePersonalizado = new File(ClassLoaders.getResource(MODELO_BRANCO_PDF).getFile());
                        guia.addTextosExtras("txtInscricao", imovel.getId() + "");
                        guiaViewer.setTemplate(templatePersonalizado);
                        guiaViewers.add(guiaViewer);
                    }
                }
            } else {
                if (quantidadePercelas > 0 && (((quantidadePercelas) % 4) > 0) && quantidadePercelas != 4) {
                    for (int i = 0; i < (4 - ((quantidadePercelas) % 4)); i++) {
                        GuiaViewer guiaViewer = new GuiaViewer(guia);
                        templatePersonalizado = new File(ClassLoaders.getResource(MODELO_BRANCO_PDF).getFile());
                        guia.addTextosExtras("txtInscricao", imovel.getId() + "");
                        guiaViewer.setTemplate(templatePersonalizado);
                        guiaViewers.add(guiaViewer);
                    }
                }
            }

            guiaViewers.addAll(gs);

        }
        try {
            if (guiaViewers != null && !guiaViewers.isEmpty()) {
                RelatorioServico.genReport(Files.fileToBytes(groupInPagesGuia(guiaViewers, "carneiptu.pdf", templatePersonalizado)));
            }

        } catch (IOException ex) {
            Logger.getLogger(IptuServico.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public List<Iptu> findIptuByImovelId(Long imovel) {
        return super.getEntityManager().createQuery("select e from " + Iptu.class.getSimpleName() + " e where e.imovel.id = " + imovel + " order by e.id DESC").getResultList();
    }

    public List<Iptu> findValidIptuByImovelId(Long imovel) {
        return super.getEntityManager().createQuery("select e from " + Iptu.class.getSimpleName() + " e where e.imovel.id = " + imovel + " and e.imovelIrregular = false order by e.id DESC").getResultList();
    }

    public List<Iptu> findIptuNaoPagos(Long imovel) {
        return this.getEntityManager().createQuery("select e from " + Iptu.class.getSimpleName() + " e where "
                + "e.imovel.id = " + imovel + " and (e.pago = false)").getResultList();
    }

    public BigDecimal valorIptu(Boolean ignorado) {
        return (BigDecimal) valorIptu(ignorado, null, "sum");
    }

    public boolean possuiIPTU(Long imovel) {
        Calendar calendar = Calendar.getInstance();
        String strDateFrom = calendar.get(Calendar.YEAR) + "/01/01";
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
        Date dateFrom = null, dateTo = null;
        try {
            dateFrom = formatter.parse(strDateFrom);
            String strDateTo = calendar.get(Calendar.YEAR) + "/12/31";
            dateTo = formatter.parse(strDateTo);
        } catch (ParseException ex) {
            Logger.getLogger(IptuServico.class.getName()).log(Level.SEVERE, null, ex);
        }
        Query createQuery;
        if (imovel == null) {
            createQuery = super.getEntityManager().createQuery("select e from " + Iptu.class.getSimpleName() + " e where e.dataIptu BETWEEN :from AND :to");
        } else {
            createQuery = super.getEntityManager().createQuery("select e from " + Iptu.class.getSimpleName() + " e where e.imovel.id = " + imovel + " and e.dataIptu BETWEEN :from AND :to");
        }
        createQuery.setParameter("from", dateFrom);
        createQuery.setParameter("to", dateTo);
        return !createQuery.getResultList().isEmpty();
    }

    public String debitoIPTU(Long imovel) {
        Query createQuery = super.getEntityManager().createQuery("select e.dataIptu from " + Iptu.class.getSimpleName() + " e where e.imovel.id = " + imovel + " and e.pago = false and e.dataIptu < '" + new SimpleDateFormat("yyyy").format(new Date()) + "-1-1' order by e.id");
        List<Date> x = createQuery.getResultList();
        String debitoAnos = "";
        for (int i = 0; i < x.size(); i++) {
            Date data = x.get(i);
            debitoAnos += new SimpleDateFormat("yyyy").format(data) + (i + 1 < x.size() ? ", " : ".");
        }
        return debitoAnos;
    }

    public List<Parcela> parcelas(Iptu iptu) {
        if (iptu != null && iptu.getId() != null) {
            String sql = "SELECT p FROM Iptu i JOIN i.parcelas p WHERE i.id = :id ORDER BY p.indice";
            Query query = getEntityManager().createQuery(sql);
            query.setParameter("id", iptu.getId());
            return (List<Parcela>) query.getResultList();
        }
        return new ArrayList<Parcela>();
    }

    public Object valorIptu(Boolean ignorado, String bairro, String functionType) {
        Calendar calendar = Calendar.getInstance();
        String strDateFrom = calendar.get(Calendar.YEAR) + "/01/01";
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
        Date dateFrom = null, dateTo = null;
        try {
            dateFrom = formatter.parse(strDateFrom);
            String strDateTo = calendar.get(Calendar.YEAR) + "/12/31";
            dateTo = formatter.parse(strDateTo);
        } catch (ParseException ex) {
            Logger.getLogger(IptuServico.class.getName()).log(Level.SEVERE, null, ex);
        }
        Query createQuery;
        String query = "select " + functionType + "(e.valorBaseCorrigido) from " + Iptu.class.getSimpleName() + " e";

        if (bairro != null) {
            query += " JOIN e.imovel as im JOIN im.enderecoImovel as ende";
        }

        query += " where ";

        if (ignorado != null) {
            query += " e.imovelIrregular = " + ignorado.booleanValue() + " and ";
        }
        if (bairro != null) {
            query += " ende.bairro = '" + bairro + "' and ";
        }
        query += " e.dataIptu BETWEEN :from AND :to";
        createQuery = super.getEntityManager().createQuery(query);
        createQuery.setParameter("from", dateFrom);
        createQuery.setParameter("to", dateTo);
        return createQuery.getSingleResult();
    }

    @Override
    public List<Iptu> find(Iptu iptu, Integer first, Integer pageSize) {

        String select = "SELECT i ";
        String sql = "FROM Iptu i ";
        String orderBy = " order by i.id asc";


        Proprietario proprietario = null;

        if (iptu.getProprietario() != null && !iptu.getProprietario().equals("")) {
            proprietario = iptu.getProprietario();
        }

        Endereco endereco = iptu.getImovel().getEnderecoImovel();

        if (proprietario != null) {

            if (proprietario.getTipoDocumento() == TipoDocumento.CPF) {
                if ((proprietario.getCpf() != null && !proprietario.getCpf().isEmpty())
                        || (proprietario.getNome() != null && !proprietario.getNome().isEmpty())) {
                    sql += " JOIN i.proprietario p ";

                }
            }
            if (proprietario.getTipoDocumento() == TipoDocumento.CNPJ) {
                if ((proprietario.getCnpj() != null && !proprietario.getCnpj().isEmpty())
                        || (proprietario.getNomeFantasia() != null && !proprietario.getNomeFantasia().isEmpty())) {
                    sql += " JOIN i.proprietario p ";
                }
            }
        }

        if (endereco != null) {
            if ((endereco.getCep() != null && !endereco.getCep().isEmpty())
                    || (endereco.getBairro() != null && !endereco.getBairro().isEmpty())
                    || (endereco.getLogradouro() != null && !endereco.getLogradouro().isEmpty())) {
                sql += " JOIN i.imovel ii JOIN ii.enderecoImovel e ";
            }
        }
        sql += " where ";
        if (endereco != null) {
            if (endereco.getCep() != null && !endereco.getCep().isEmpty()) {
                sql += " lower(e.cep) like lower(:cep) and ";
            }
            if (endereco.getLogradouro() != null && !endereco.getLogradouro().isEmpty()) {
                sql += " lower(e.logradouro) like lower(:logradouro) and ";
            }
            if (endereco.getBairro() != null && !endereco.getBairro().isEmpty()) {
                sql += " lower(e.bairro) like lower(:bairro) and ";
            }
        }

        if (proprietario != null) {
            if (proprietario.getTipoDocumento() == TipoDocumento.CPF) {
                if (proprietario.getCpf() != null && !proprietario.getCpf().isEmpty()) {
                    sql += " lower(p.cpf) like lower(:cpf) and ";
                }
                if (proprietario.getNome() != null && !proprietario.getNome().isEmpty()) {
                    sql += " lower(p.nome) like lower(:nome) and ";
                }
            }

            if (proprietario.getTipoDocumento() == TipoDocumento.CNPJ) {
                if (proprietario.getCnpj() != null && !proprietario.getCnpj().isEmpty()) {
                    sql += " lower(p.cnpj) like lower(:cnpj) and ";
                }
                if (proprietario.getNomeFantasia() != null && !proprietario.getNomeFantasia().isEmpty()) {
                    sql += " lower(p.nomeFantasia) like lower(:nomeFantasia) and ";
                }
            }
        }


        if (iptu.getImovel().getInscricaoCadastralD() != null) {
            sql += " ii.inscricaoCadastralD = :d and ";
        }
        if (iptu.getImovel().getInscricaoCadastralS() != null) {
            sql += " ii.inscricaoCadastralS = :s and ";
        }
        if (iptu.getImovel().getInscricaoCadastralQuadra() != null) {
            sql += " ii.inscricaoCadastralQuadra = :q and ";
        }
        if (iptu.getImovel().getInscricaoCadastralLote() != null) {
            sql += " ii.inscricaoCadastralLote = :l and ";
        }
        if (iptu.getImovel().getInscricaoCadastralUnid() != null) {
            sql += " ii.inscricaoCadastralUnid = :u and ";
        }

        sql += " i.ativo = true and 1=1 ";

        Query query = getEntityManager().createQuery(select.concat(sql).concat(orderBy));

        if (proprietario != null) {
            if (proprietario.getTipoDocumento() == TipoDocumento.CPF) {
                if (proprietario.getCpf() != null && !proprietario.getCpf().isEmpty()) {
                    query.setParameter("cpf", proprietario.getCpf());
                }
                if (proprietario.getNome() != null && !proprietario.getNome().isEmpty()) {
                    query.setParameter("nome", "%" + proprietario.getNome() + "%");
                }
            }

            if (proprietario.getTipoDocumento() == TipoDocumento.CNPJ) {
                if (proprietario.getCnpj() != null && !proprietario.getCnpj().isEmpty()) {
                    query.setParameter("cnpj", proprietario.getCnpj());
                }
                if (proprietario.getNomeFantasia() != null && !proprietario.getNomeFantasia().isEmpty()) {
                    query.setParameter("nomeFantasia", "%" + proprietario.getNomeFantasia() + "%");

                }
            }
        }
        if (endereco != null) {
            if (endereco.getCep() != null && !endereco.getCep().isEmpty()) {
                query.setParameter("cep", endereco.getCep());
            }

            if (endereco.getLogradouro() != null && !endereco.getLogradouro().isEmpty()) {
                query.setParameter("logradouro", endereco.getLogradouro());
            }

            if (endereco.getBairro() != null && !endereco.getBairro().isEmpty()) {
                query.setParameter("bairro", endereco.getBairro());
            }
        }


        if (iptu.getImovel().getInscricaoCadastralD() != null) {
            query.setParameter("d", iptu.getImovel().getInscricaoCadastralD());
        }
        if (iptu.getImovel().getInscricaoCadastralS() != null) {
            query.setParameter("s", iptu.getImovel().getInscricaoCadastralS());
        }
        if (iptu.getImovel().getInscricaoCadastralQuadra() != null) {
            query.setParameter("q", iptu.getImovel().getInscricaoCadastralQuadra());
        }
        if (iptu.getImovel().getInscricaoCadastralLote() != null) {
            query.setParameter("l", iptu.getImovel().getInscricaoCadastralLote());
        }
        if (iptu.getImovel().getInscricaoCadastralUnid() != null) {
            query.setParameter("u", iptu.getImovel().getInscricaoCadastralUnid());
        }
        setQuantidadeRegistros(sql, query, proprietario, endereco, iptu.getImovel());
        setParameters(proprietario, query, endereco, iptu.getImovel());
        query.setFirstResult(first).setMaxResults(pageSize);

        return query.getResultList();
    }

    @Transient
    private void setParameters(Proprietario proprietario, Query query, Endereco endereco, Imovel imovel) {
        if (proprietario != null) {
            if (proprietario.getTipoDocumento() == TipoDocumento.CPF) {
                if (proprietario.getCpf() != null && !proprietario.getCpf().isEmpty()) {
                    query.setParameter("cpf", proprietario.getCpf());
                }
                if (proprietario.getNome() != null && !proprietario.getNome().isEmpty()) {
                    query.setParameter("nome", "%" + proprietario.getNome() + "%");
                }
            }
            if (proprietario.getTipoDocumento() == TipoDocumento.CNPJ) {
                if (proprietario.getCnpj() != null && !proprietario.getCnpj().isEmpty()) {
                    query.setParameter("cnpj", proprietario.getCnpj());
                }
                if (proprietario.getNomeFantasia() != null && !proprietario.getNomeFantasia().isEmpty()) {
                    query.setParameter("nomeFantasia", "%" + proprietario.getNomeFantasia() + "%");
                }
            }
        }
        if (endereco != null) {
            if (endereco.getCep() != null && !endereco.getCep().isEmpty()) {
                query.setParameter("cep", endereco.getCep());
            }

            if (endereco.getNumero() != null && !endereco.getNumero().isEmpty()) {
                query.setParameter("numero", "%" + endereco.getNumero() + "%");
            }

            if (endereco.getLogradouro() != null && !endereco.getLogradouro().isEmpty()) {
                query.setParameter("logradouro", "%" + endereco.getLogradouro() + "%");
            }

            if (endereco.getBairro() != null && !endereco.getBairro().isEmpty()) {
                query.setParameter("bairro", "%" + endereco.getBairro() + "%");
            }
        }

        if (imovel.getInscricaoCadastralD() != null) {
            query.setParameter("d", imovel.getInscricaoCadastralD());
        }

        if (imovel.getInscricaoCadastralS() != null) {
            query.setParameter("s", imovel.getInscricaoCadastralS());
        }

        if (imovel.getInscricaoCadastralQuadra() != null) {
            query.setParameter("q", imovel.getInscricaoCadastralQuadra());
        }

        if (imovel.getInscricaoCadastralUnid() != null) {
            query.setParameter("u", imovel.getInscricaoCadastralUnid());
        }

        if (imovel.getInscricaoCadastralLote() != null) {
            query.setParameter("l", imovel.getInscricaoCadastralLote());
        }
    }

    private void setQuantidadeRegistros(String sql, Query query, Proprietario proprietario, Endereco endereco, Imovel imovel) {

        String select = "SELECT count(i) ";

        query = getEntityManager().createQuery(select + sql);

        setParameters(proprietario, query, endereco, imovel);

        quantidadeRegistrosResults = (Long) query.getSingleResult();
    }

    public Date getDataAtual() {
        return new Date();
    }

    public List<Long> findAllImoveis() {
        return getEntityManager().createQuery("select imv.id from Iptu iptu join iptu.imovel imv where iptu.imovelIrregular <> TRUE").getResultList();
    }
}
